/**
 * 
 */
package com.novartis.nims.imagedc.dataaccess;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.URI;
import java.rmi.RemoteException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.axis.types.URI.MalformedURIException;
import org.apache.log4j.Logger;
import org.cagrid.transfer.context.client.TransferServiceContextClient;
import org.cagrid.transfer.context.client.helper.TransferClientHelper;
import org.cagrid.transfer.context.stubs.types.TransferServiceContextReference;
import org.cagrid.transfer.descriptor.Status;

import com.novartis.nims.beans.FileSubmissionInformationType;
import com.novartis.nims.beans.FileTransportType;
import com.novartis.nims.beans.IdentifierListType;
import com.novartis.nims.beans.IdentifierType;
import com.novartis.nims.beans.ImageFileListType;
import com.novartis.nims.beans.ImageFileType;
import com.novartis.nims.beans.InsertScanMessageType;
import com.novartis.nims.beans.InsertScanResponseMessageType;
import com.novartis.nims.beans.InsertScanType;
import com.novartis.nims.beans.MessageHeaderType;
import com.novartis.nims.beans.MessageHeaderTypeEnvironment;
import com.novartis.nims.beans.ScanObjectType;
import com.novartis.nims.beans.UploadScanMessageType;
import com.novartis.nims.beans.UploadScanType;
import com.novartis.nims.beans.ValueListType;
import com.novartis.nims.beans.ValueType;
import com.novartis.nims.imagedc.logic.ImageResource;
import com.novartis.nims.imagedc.logic.Scan;
import com.novartis.nims.imagedc.util.SHA1Hash;

/**
 * @author snydejo2
 *
 */
public class ScanServiceDao {

	private Logger logger = Logger.getLogger(ScanServiceDao.class);	
	private NimsScanClient client;

	/**
	 * @throws RemoteException 
	 * @throws MalformedURIException 
	 * 
	 */
	public ScanServiceDao(String serviceUrl) throws MalformedURIException, RemoteException {
		client = new NimsScanClient(serviceUrl);
	}

	public void submitScan(Scan scan) throws Exception {
		logger.debug("Submitting a scan to Scan Service");

		// UPLOAD METADATA AND GET SCANID FROM NIMS
		InsertScanResponseMessageType aNewScan = client.insert(createInsertMessage(scan));
		logger.debug("New InsertScanMessage created");
		
		String scanID = aNewScan.getMessageBody().getScanId();
		logger.debug("Submitted scan ID =" + scanID);

		// UPLOAD DICOM ZIP USING SCANID
		UploadScanMessageType uploadScan = createUploadMessage(scan, scanID);
		TransferServiceContextReference ref1 = client.uploadFiles(uploadScan);

		// use the EPR from the reference to create a client to talk to my resource
		TransferServiceContextClient tclient1 = new TransferServiceContextClient(ref1.getEndpointReference());

		// use the helper to get the output stream that I can upload
		// my data with
		//DataDescriptor dd = new DataDescriptor(null, scanID);
		//Read from an input stream
		String filename = uploadScan.getMessageBody().getFileSubmissionInformation().getFileName();
		FileInputStream fileIs = new FileInputStream(filename);
		InputStream is = new BufferedInputStream(fileIs);                              
		TransferClientHelper.putData(is, new File(filename).length() , tclient1.getDataTransferDescriptor());

		// tell the resource that the data has been uploaded.
		tclient1.setStatus(Status.Staged);
	}

	private InsertScanMessageType createInsertMessage(Scan scan) throws NoSuchAlgorithmException, IOException {	
		InsertScanMessageType aScanMessage = new InsertScanMessageType(); 

		InsertScanType messageBody = new InsertScanType();
		messageBody.setScanData(createScanObject(scan)); 
		//messageBody.setScanKPI(createScanKPI());
		messageBody.setDomainIdentifiers(createDomainIdentifiers(scan));
		messageBody.setDomainQualifiers(createDomainQualifiers(scan));

		aScanMessage.setMessageBody(messageBody);
		aScanMessage.setMessageHeader(createHeader(true));
		
		return aScanMessage;
	}

	private MessageHeaderType createHeader(boolean request) {
		//TODO update this method with appropriate values
		// request or reply change header data  if (request) {	}

		MessageHeaderType messageHeader = new MessageHeaderType();
		messageHeader.setMessageVersionMajor("1");
		messageHeader.setMessageVersionMinor("0");
		//messageHeader.setSenderID("0000");
		//messageHeader.setSenderName("www.novartis.com");
		messageHeader.setEnvironment(MessageHeaderTypeEnvironment.fromString("DEV"));
		//messageHeader.setTransactionId("9999999");
		//messageHeader.setCorrelationId("9999999");
		messageHeader.setTimeStamp(java.util.Calendar.getInstance(java.util.Locale.US));
		//messageHeader.setExpiration(java.util.Calendar.getInstance(java.util.Locale.CANADA));
		//messageHeader.setApplicationData(new MessageHeaderTypeApplicationData()); 
		return messageHeader;
	}

	private ScanObjectType createScanObject(Scan scan) throws NoSuchAlgorithmException, IOException {
		ScanObjectType aScanObject = new ScanObjectType();

		//TODO: make these config items
		aScanObject.setDomainName("Clinical");
		aScanObject.setObjectVersionMajor("0"); 
		aScanObject.setObjectVersionMinor("7");


		aScanObject.setScanName(scan.getScanName());
		aScanObject.setRepeatNumber(BigInteger.ONE);
		aScanObject.setImageFiles(createImageFiles(scan));
		aScanObject.setScanQualifiers(createScanQualifiers());
		return aScanObject;  
	}


	private ImageFileListType createImageFiles(Scan scan) throws NoSuchAlgorithmException, IOException {
		ImageFileType anImageFile;
		ImageFileListType anImageFileList = new ImageFileListType();

		List<ImageFileType> imageFileList = new ArrayList<ImageFileType>();

		for (ImageResource image : scan) {
			URI relativePath = scan.getBasePath().relativize(image.getUri());

			//calculate hash value of referenced file
			FileInputStream hashStream = null;
			String hash;
			try {
				hashStream = new FileInputStream(new File(image.getUri()));
				hash = SHA1Hash.hash(hashStream);
			} finally {
				try { if (hashStream != null) hashStream.close(); } catch (IOException e) {}
			}

			anImageFile = new ImageFileType();
			anImageFile.setFileReference(relativePath.getPath());
			anImageFile.setHashValue(hash);

			imageFileList.add(anImageFile);
		}

		anImageFileList.setHashMethod("sha1hash");
		anImageFileList.setFileTransport(FileTransportType.fromString("attached") );
		anImageFileList.setImageFilesCount(imageFileList.size()); 
		anImageFileList.setImageFilesArray(imageFileList.toArray(new ImageFileType[] {}));

		return anImageFileList;
	}

	private ValueListType createScanQualifiers() {
		ValueListType scanQualifiers = new ValueListType();
		scanQualifiers.setValueListName("CDMS");
		scanQualifiers.setValuesCount(0);
		return scanQualifiers;
	}
	
	private IdentifierListType createDomainIdentifiers(Scan scan){
		IdentifierType aIdentifier;
		IdentifierListType aIdentifierList = new IdentifierListType();
		
		//StudyID, SubjectID, VisitNumber, VisitName, VisitRepeatNumber
		aIdentifierList.setIdentifierListName("DomainIDs");
		aIdentifierList.setIdentifiersCount(4);
		
		List<IdentifierType> aIdentifierArrayList = new ArrayList<IdentifierType>();
		
		aIdentifier = new IdentifierType();
		aIdentifier.setElementName("StudyId");
		aIdentifier.setElementId(scan.getStudyId());
		aIdentifierArrayList.add(aIdentifier);
		
		aIdentifier = new IdentifierType();
		aIdentifier.setElementName("SubjectID");
		aIdentifier.setElementId(scan.getSiteSubjectId());
		aIdentifierArrayList.add(aIdentifier);
		
		aIdentifier = new IdentifierType();
		aIdentifier.setElementName("VisitNumber");
		aIdentifier.setElementId(scan.getVisitNumber());
		aIdentifierArrayList.add(aIdentifier);
		
		aIdentifier = new IdentifierType();
		aIdentifier.setElementName("VisitRepeatNumber");
		aIdentifier.setElementId(scan.getVisitRepeatNumber());
		aIdentifierArrayList.add(aIdentifier);
		
		aIdentifierList.setIdentifiersArray(aIdentifierArrayList.toArray(new IdentifierType[] {}));

		return aIdentifierList;
	}

	private  ValueListType createDomainQualifiers(Scan scan) {
		ValueType aValue;
		ValueListType aValueList = new ValueListType();
		
		aValueList.setValueListName("ContextIG");
		aValueList.setValuesCount(1);
		
		List<ValueType> aValueArrayList = new ArrayList<ValueType>();
		aValue = new ValueType();
		aValue.setName("VISIT");
		aValue.setValue(scan.getVisitName());
		aValueArrayList.add(aValue);

		aValueList.setValuesArray(aValueArrayList.toArray(new ValueType[] {}));
		
		return aValueList;
	}

	public UploadScanMessageType createUploadMessage(Scan scan, String scanId) throws IOException {	
		UploadScanMessageType anUploadMessage = new UploadScanMessageType(); 
		UploadScanType messageBody = new UploadScanType();

		messageBody.setScanId(scanId);
		messageBody.setFileSubmissionInformation(createFileSubmission(scan));
		anUploadMessage.setMessageBody(messageBody);
		anUploadMessage.setMessageHeader(createHeader(true));
		return anUploadMessage;
	}

	private FileSubmissionInformationType createFileSubmission(Scan scan) throws IOException {
		//create zip file of images for transfer
		//TODO file reading and writing are blocking operations. implement progress notifications
		File submissionFile = File.createTempFile("scan-", ".zip");
		submissionFile.deleteOnExit();
		
		ZipOutputStream outStream = null;
		try {
			outStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(submissionFile)));
			BufferedInputStream inStream = null;
			int bufferLength = 1024;
			byte[] buffer = new byte[bufferLength];
			for (ImageResource image : scan) {
				String relativePath = scan.getBasePath().relativize(image.getUri()).getPath();
				outStream.putNextEntry(new ZipEntry(relativePath));

				try {
					inStream = new BufferedInputStream(new FileInputStream(new File(image.getUri())), bufferLength);
					int count;
					while ((count = inStream.read(buffer)) != -1) {
						outStream.write(buffer, 0, count);
					}
				} finally {
					inStream.close();
				}
			}
		} finally {
			outStream.close();
		}
		
		FileSubmissionInformationType aFileSubmission = new FileSubmissionInformationType();
		aFileSubmission.setCompressed(true);
		aFileSubmission.setFileName(submissionFile.getCanonicalPath());
		aFileSubmission.setFileType("zip");
		aFileSubmission.setNumberOfFiles(1);	

		return aFileSubmission;
	}
	// 
	//
	// private  ValueListType createScanKPI(){
	//		ValueType aValue;
	//		int i=5;
	//	    ValueListType aValueList = new ValueListType();
	//	    aValueList.setValueListName("ScanKPI");
	//	    aValueList.setValuesCount(i);
	//		ValueType[] aValueArray = new ValueType[i];
	//		i=0;
	//		aValue = new ValueType();
	//		aValue.setName("Date of Shippment");
	//		aValue.setValue("2008-09-10T14:34:23");
	//		aValueArray[i++]= aValue;
	//		aValue = new ValueType();
	//		aValue.setName("Date of CRO arrival");
	//		aValue.setValue("2008-09-02T15:34:23");
	//		aValueArray[i++]= aValue;
	//		aValue = new ValueType();
	//		aValue.setName("CRO Quality Check");
	//		aValue.setValue("2008-09-03T16:34:23");
	//		aValueArray[i++]= aValue;
	//		aValue = new ValueType();
	//		aValue.setName("Date of Scan Analysis Completed");
	//		aValue.setValue("2008-09-04T16:34:23");
	//		aValueArray[i++]= aValue;		
	//		aValue = new ValueType();
	//		aValue.setName("Scan Quality Flag");
	//		aValue.setValue("No");
	//		aValueArray[i]= aValue;			
	//		return aValueList;
	//}

}
